﻿//===========================[ 7 / 6 / 2011 LVI ]=============================
//				Copyright c Vitaly Lyaschenko <SkyChameleon@gmail.com>
//								Source file
// Purpose: основные операции над шейдерами и шейдерной программой
//=============================================================================
//#include "gl3com.h"
#include <GL/glew.h>
#include <GL/wglew.h>

#include "cwLogger.h"
#include "cFileManager.h"
#include "cShaderProgram.h"


cShaderProgram::cShaderProgram( const char* VertFile, const char* fragFile ) :
     m_Program(-1),status(false)
{
    cShader _vertShader, _fragShader;

    _vertShader.load( VertFile,GL_VERTEX_SHADER );
    _fragShader.load( fragFile,GL_FRAGMENT_SHADER );

    create();
    attach( _vertShader );
    attach( _fragShader );

    link();
}

cShaderProgram::~cShaderProgram(void)
{
    release();
}

void cShaderProgram::create()
{
    //ASSERT(m_Program);
    m_Program = glCreateProgram();
}

void cShaderProgram::release()
{
    if ( glIsProgram( m_Program ) == GL_TRUE )
    {
        glUseProgram(0);
        glDeleteProgram( m_Program );
    }
    status = false;
    m_Program = 0;
}

void cShaderProgram::attach( cShader &shader )
{
    glAttachShader( m_Program, shader.getShader() );
}

bool cShaderProgram::link()
{
    glLinkProgram(m_Program);

    return checkStatus(GL_LINK_STATUS) == GL_TRUE;
}

bool cShaderProgram::validate()
{
    glValidateProgram(m_Program);

    return checkStatus(GL_VALIDATE_STATUS) == GL_TRUE;
}

void cShaderProgram::UseProgram()
{
    glUseProgram( m_Program );
}

void cShaderProgram::UnuseProgram()
{
    glUseProgram( 0 );
}

GLint cShaderProgram::checkStatus( GLenum param )
{
    GLint  checkStat, length;
    GLchar buffer[1024];

    glGetProgramiv( m_Program, param, &checkStat );

    if (checkStat != GL_TRUE)
    {
        glGetProgramInfoLog( m_Program, ARRAYSIZE(buffer), &length, buffer );

        cwLogger::writeERROR("Shader program: " + std::string(buffer) );
        this->status = false;
    }
    this->status = true;

    return checkStat;
}
///////////////////////////////////////////////////////////////////////////
// возвращает адрес юниформа в программе
int cShaderProgram::getLocationUniform( const char* nameUnif )
{
    ASSERT( nameUnif );

    int location = glGetUniformLocation( m_Program, nameUnif );

    if ( location < 0 )
        return -1;

    return location;
}
///////////////////////////////////////////////////////////////////////////
// http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml
///////////////////////////////////////////////////////////////////////////
int cShaderProgram::setParameter( const char* nameUnif, int *V, int num )
{
    ASSERT( nameUnif );
    ASSERT( V );
    ASSERT( num > 0 && num < 5 );

    int locationUnif = -1;

    switch( num )
    {
    case 1:
        {
            locationUnif = glGetUniformLocation( m_Program, nameUnif );
            glUniform1iv( locationUnif, num, V );
        } break;
    case 2:
        {
            locationUnif = glGetUniformLocation( m_Program, nameUnif );
            glUniform2iv( locationUnif, num, V );
        } break;
    case 3:
        {
            locationUnif = glGetUniformLocation( m_Program, nameUnif );
            glUniform3iv( locationUnif, num, V );
        } break;

    case 4:
        {
            locationUnif = glGetUniformLocation( m_Program, nameUnif );
            glUniform4iv( locationUnif, num, V );
        } break;
    }
    return locationUnif;
}

int cShaderProgram::setParameter( const char* nameUnif, float *V, int num )
{
    ASSERT( nameUnif );
    ASSERT( V );
    ASSERT( num > 0 && num < 5 );

    int locationUnif = -1;

    switch( num )
    {
    case 1: 
        {
            locationUnif = glGetUniformLocation( m_Program, nameUnif );
            glUniform1fv( locationUnif, num, V );
        } break;
    case 2: 
        {
            locationUnif = glGetUniformLocation( m_Program, nameUnif );
            glUniform2fv( locationUnif, num, V );
        } break;
    case 3: 
        {
            locationUnif = glGetUniformLocation( m_Program, nameUnif );
            glUniform3fv( locationUnif, num, V );
        } break;

    case 4:
        {
            locationUnif = glGetUniformLocation( m_Program, nameUnif );
            glUniform4fv( locationUnif, num, V );
        } break;
    }
    return locationUnif;
}

///////////////////////////////////////////////////////////////////////////
// UNIFORM Matrix 2x2
// поумолчанию матрицы инвертируются перед передачей в шейдер!
int cShaderProgram::setUniformMatrix2fv( const char* nameUnif, const GLfloat *val, GLsizei count, bool transpose )
{
    ASSERT( nameUnif );

    int location = glGetUniformLocation( m_Program, nameUnif );
    if ( location < 0 )
        return false;
    glUniformMatrix2fv( location, count, transpose, val);

    return location;
}

bool cShaderProgram::setUniformMatrix2fv( const int location, const GLfloat *val, GLsizei count, bool transpose )
{
    if ( location < 0 )
        return false;

    glUniformMatrix2fv( location, count, transpose, val);

    return true;
}
///////////////////////////////////////////////////////////////////////////
// UNIFORM Matrix 3x3
int cShaderProgram::setUniformMatrix3fv( const char* nameUnif, const GLfloat *val, GLsizei count, bool transpose )
{
    ASSERT( nameUnif );

    int location = glGetUniformLocation( m_Program, nameUnif );

    if ( location < 0 )
        return false;
    glUniformMatrix3fv( location, count, transpose, val );

    return location;
}

bool cShaderProgram::setUniformMatrix3fv( const int location, const GLfloat *val, GLsizei count, bool transpose )
{
    if ( location < 0 )
        return false;

    glUniformMatrix3fv( location, count, transpose, val );

    return true;
}
///////////////////////////////////////////////////////////////////////////
// UNIFORM Matrix 4x4
int cShaderProgram::setUniformMatrix4fv( const char* nameUnif, const GLfloat *val, GLsizei count, bool transpose )
{
    ASSERT( nameUnif );

    int location = glGetUniformLocation( m_Program, nameUnif );

    if ( location < 0 )
        return false;

    glUniformMatrix4fv( location, count, transpose, val );

    return location;
}

bool cShaderProgram::setUniformMatrix4fv( const int location, const GLfloat *val, GLsizei count, bool transpose )
{
    if ( location < 0 )
        return false;

    glUniformMatrix4fv( location, count, transpose, val );

    return true;
}
///////////////////////////////////////////////////////////////////////////
// UNIFORM 1i
int cShaderProgram::setUniform1i( const char* nameUnif, int v0 )
{
    ASSERT( nameUnif );

    int location = glGetUniformLocation( m_Program, nameUnif );

    if ( location < 0 )
        return false;

    glUniform1i( location, v0 );

    return location;
}

bool cShaderProgram::setUniform1i( const int location, int v0 )
{
    if ( location < 0 )
        return false;

    glUniform1i( location, v0 );

    return true;
}
///////////////////////////////////////////////////////////////////////////
// UNIFORM 2i
int cShaderProgram::setUniform2i( const char* nameUnif, vec2 V )
{
    return setUniform2i( nameUnif, V.x, V.y );
}

int cShaderProgram::setUniform2i( const char* nameUnif, int v0, int v1 )
{
    ASSERT( nameUnif );

    int location = glGetUniformLocation( m_Program, nameUnif );

    if ( location < 0 )
        return false;

    glUniform2i( location, v0, v1 );

    return location;
}

bool cShaderProgram::setUniform2i( const int location, int v0, int v1 )
{
    if ( location < 0 )
        return false;

    glUniform2i( location, v0, v1 );

    return true;
}
///////////////////////////////////////////////////////////////////////////
// UNIFORM 3i
int cShaderProgram::setUniform3i( const char* nameUnif, vec3 V )
{
    return setUniform3i( nameUnif, V.x, V.y, V.z );
}

int cShaderProgram::setUniform3i( const char* nameUnif, int v0, int v1, int v2 )
{
    ASSERT( nameUnif );

    int location = glGetUniformLocation( m_Program, nameUnif );

    if ( location < 0 )
        return false;

    glUniform3i( location, v0, v1, v2 );

    return location;
}

bool cShaderProgram::setUniform3i( const int location, int v0, int v1, int v2 )
{
    if ( location < 0 )
        return false;

    glUniform3i( location, v0, v1, v2 );

    return true;
}
///////////////////////////////////////////////////////////////////////////
// UNIFORM 4i
int cShaderProgram::setUniform4i( const char* nameUnif, vec4 V )
{
    return setUniform4i( nameUnif, V.x, V.y, V.z, V.w );
}

int cShaderProgram::setUniform4i( const char* nameUnif, int v0, int v1, int v2, int v3 )
{
    ASSERT( nameUnif );

    int location = glGetUniformLocation( m_Program, nameUnif );

    if ( location < 0 )
        return false;

    glUniform4i( location, v0, v1, v2, v3 );

    return true;
}

bool cShaderProgram::setUniform4i( const int location, int v0, int v1, int v2, int v3 )
{
    if ( location < 0 )
        return false;

    glUniform4i( location, v0, v1, v2, v3 );

    return true;
}

///////////////////////////////////////////////////////////////////////////
// UNIFORM 1F
int cShaderProgram::setUniform1f( const char* nameUnif, float v0 )
{
    ASSERT( nameUnif );

    int location = glGetUniformLocation( m_Program, nameUnif );

    if ( location < 0 )
        return false;

    glUniform1f( location, v0 );

    return location;
}

bool cShaderProgram::setUniform1f( const int location, float v0 )
{
    if ( location < 0 )
        return false;

    glUniform1f( location, v0 );

    return true;
}
///////////////////////////////////////////////////////////////////////////
// UNIFORM 2F
int cShaderProgram::setUniform2f( const char* nameUnif, vec2 V )
{
    return setUniform2f( nameUnif, V.x, V.y );
}

int cShaderProgram::setUniform2f( const char* nameUnif, float v0, float v1 )
{
    ASSERT( nameUnif );

    int location = glGetUniformLocation( m_Program, nameUnif );

    if ( location < 0 )
        return false;

    glUniform2f( location, v0, v1 );

    return location;
}

bool cShaderProgram::setUniform2f( const int location, float v0, float v1 )
{
    if ( location < 0 )
        return false;

    glUniform2f( location, v0, v1 );

    return true;
}

///////////////////////////////////////////////////////////////////////////
// UNIFORM 3F
int cShaderProgram::setUniform3f( const char* nameUnif, vec3 V )
{
    return setUniform3f( nameUnif, V.x, V.y, V.z );
}

int cShaderProgram::setUniform3f( const char* nameUnif, float v0, float v1, float v2 )
{
    ASSERT( nameUnif );

    int location = glGetUniformLocation( m_Program, nameUnif );

    if ( location < 0 )
        return false;

    glUniform3f( location, v0, v1, v2 );

    return location;
}

bool cShaderProgram::setUniform3f( const int location, float v0, float v1, float v2 )
{
    if ( location < 0 )
        return false;

    glUniform3f( location, v0, v1, v2 );

    return true;
}
///////////////////////////////////////////////////////////////////////////
// UNIFORM 4F
int cShaderProgram::setUniform4f( const char* nameUnif, vec4 V )
{
    return setUniform4f( nameUnif, V.x, V.y, V.z, V.w );
}

int cShaderProgram::setUniform4f( const char* nameUnif, float v0, float v1, float v2, float v3 )
{
    ASSERT( nameUnif );

    int location = glGetUniformLocation( m_Program, nameUnif );

    if ( location < 0 )
        return false;

    glUniform4f( location, v0, v1, v2, v3 );

    return location;
}

bool cShaderProgram::setUniform4f( const int location, float v0, float v1, float v2, float v3 )
{
    if ( location < 0 )
        return false;

    glUniform4f( location, v0, v1, v2, v3 );

    return true;
}